// *********************************************************
//      Copyright (c) Kosta Gaitanis
//      All rights reserved
//
//      k.gaitanis@gmail.com
//
// *********************************************************

#include "Trajectory.h"
#include "Sphere.h"
#include "Cylinder.h"

#include "Planet.h"

const double PI = 3.1415926;
const double TO_RADIAN = M_PI/180.0;
const double PI_2 = M_PI*2;

Planet::Planet(Planet::Planets planet) :
    Item(),
    m_radius(0),
    m_axialTilt(0),
    m_distanceFromCenter(0),
    m_selfRotationPeriod(0),
    m_rotationPeriod(0),
    m_orbitInclination(0)
{
    double radius = 0;
    double distanceToCenter = 0;
    double axialTilt = 0;
    double selfRotation = 0;
    double rotation = 0;
    double inclination = 0;
    QString name;
    switch (planet) {
    case(Sun):
        name = "Sun";
        radius = 100000 / 2.0;
        //radius = 10000 / 2.0;
        distanceToCenter = 0.0;
        axialTilt = 7.25;       // degrees
        selfRotation = 609.12;  // hours
        rotation = 0.069;       // years
        inclination = 0.0;      // degrees        
        break;
    case(Mercury):
        name = "Mercury";
        radius = 2440;  // kilometers
        distanceToCenter = 0.387;  // Astronomical units = 149,598,000 kms
        axialTilt = 0.0;            // degrees
        selfRotation = 1407.6;         // hours
        rotation = 0.241;           // years = 365.256 days
        inclination = 6.34;
        break;
    case(Venus):
        name = "Venus";
        radius = 6051;
        distanceToCenter = 0.722;
        axialTilt = 177.36;
        selfRotation = 5832.5;
        rotation = 0.615;
        inclination = 2.19;
        break;
    case(Earth):
        name = "Earth";
        radius = 6378;
        distanceToCenter = 1.0;
        //axialTilt = 23.4;
        axialTilt = 4;
        selfRotation = 23.93;
        rotation = 1.0;
        inclination = 1.57;
        //inclination = 45;
        break;
    case(Mars):
        name = "Mars";
        radius = 3397;
        distanceToCenter = 1.52;
        axialTilt = 25.19;
        selfRotation = 24.62;
        rotation = 1.881;
        inclination = 1.67;
        break;
    case(Jupiter):
        name = "Jupiter";
        radius = 71492;
        //radius = 7149;
        distanceToCenter = 5.2;
        axialTilt = 3.13;
        selfRotation = 9.93;
        rotation = 11.86;
        inclination = 0.32;
        break;
    case(Io):
        name = "Io";
        radius = 1821.3;
        distanceToCenter = 0.04 * 7;
        axialTilt = 3.13;
        selfRotation = 1.769;
        rotation = 1.769/365;
        inclination = 2.21;
        break;
    case(Europa):
        name = "Europa";
        radius = 1560.8;
        distanceToCenter = 0.045 * 7;
        axialTilt = 3.13;
        selfRotation = 9.93;
        rotation = 3.55/365;
        inclination = 0.47;
        break;
    case(Ganimede):
        name = "Ganimede";
        radius = 2634.1;
        distanceToCenter = 0.072 * 7;
        axialTilt = 0.33;
        selfRotation = 7.15;
        rotation = 7.15/365;
        inclination = 0.2;
        break;
    case(Callisto):
        name = "Callisto";
        radius = 2410;
        distanceToCenter = 0.12 * 7;
        axialTilt = 3.13;
        selfRotation = 16.69;
        rotation = 16.69/365;
        inclination = 0.192;
        break;
    case(Saturn):
        name = "Saturn";
        radius = 2410;
        distanceToCenter = 9.58;
        axialTilt = 26.73;
        selfRotation = 10.66;
        rotation = 29.46;
        inclination = 5.51;
        break;
    case(Uranus):
        name = "Uranus";
        radius = 25559;
        distanceToCenter = 19.2;
        axialTilt = 97.77;
        selfRotation = 17.24;
        rotation = 84.32;
        inclination = 1.02;
        break;
    case(Neptune):
        name = "Neptune";
        radius = 24764;
        distanceToCenter = 30.1;
        axialTilt = 28.32;
        selfRotation = 16.11;
        rotation = 164.8;
        inclination = 0.72;
        break;
    case(Pluto):
        name = "Pluto";
        radius = 1160;
        distanceToCenter = 39.5;
        axialTilt = 122.53;
        selfRotation = 153.29;
        rotation = 248.1;
        inclination = 18.7;
        break;
    case(Moon):
        name = "Moon";
        radius = 1738;
        distanceToCenter = 0.0026 * 30;
        axialTilt = 6.68;
        selfRotation = 655.73;
        rotation = 0.0748;
        inclination = 5.145;
        break;
    }

    setName(name);
    setRadius(radius);
    setDistanceFromCenter(distanceToCenter);
    setAxialTilt(axialTilt);
    setRotationPeriod(rotation);
    setSelfRotationPeriod(selfRotation);
    setOrbitInclination(inclination);
    Item* sphere = new Sphere(getRadius(), 10, getName());
    sphere->setShareMaterialWithParent(true);
    addChild(sphere);

    if (planet == Sun) {
        getMaterial()->setEmission(Qt::white);  // sun glows in the dark
    }

    getMaterial()->setSpecular(QColor(150,150,150));
    getMaterial()->setShininess(20);
}

Planet::~Planet()
{
}

void Planet::setRadius(double radius)
{
    m_radius = radius;
}

void Planet::setAxialTilt(double axialTilt)
{
    // degrees
    m_axialTilt = axialTilt;
}

void Planet::setDistanceFromCenter(double distance)
{
    // AU
    m_distanceFromCenter = distance;
}

void Planet::setSelfRotationPeriod(double period)
{
    // hours
    m_selfRotationPeriod = period;
}

void Planet::setRotationPeriod(double period)
{
    // years
    m_rotationPeriod = period;
}

void Planet::setOrbitInclination(double inclination)
{
    // degrees
    m_orbitInclination = inclination;
}

double Planet::getRadius() const
{
    return m_radius / 149598.0;  // convert to AU
}

double Planet::getAxialTilt() const
{
    return m_axialTilt;
}

double Planet::getDistanceFromCenter() const
{
    return m_distanceFromCenter * 2.0;
}

double Planet::getSelfRotationPeriod() const
{
    return m_selfRotationPeriod;
}

double Planet::getRotationPeriod() const
{
    return m_rotationPeriod;
}

double Planet::getOrbitInclation() const
{
    return m_orbitInclination;
}

void Planet::addSatelite(Planet *satelite)
{
    QString trajectoriesName("Satelite Trajectories");
    if (!hasChild(satelite)) {

        Item* trajectory = new Trajectory(satelite->m_distanceFromCenter, QString(satelite->getName()));

        /* FIXME complete the code */
        // setup the transform to be applied on the trajectory
        // use the orbit inclination parameter
        double I = satelite->m_orbitInclination*TO_RADIAN;
        Matrix m = Matrix::rotationX(I);
        trajectory->setTransform(m);

        /* STOP here */
        if (!hasChild(trajectoriesName)) {
            Item* trajectories = new Item(trajectoriesName);
            addChild(trajectories);
            trajectories->setMaterial(*trajectory->getMaterial());
        }

        trajectory->setShareMaterialWithParent(true);
        getChild(trajectoriesName)->addChild(trajectory);

        addChild(satelite);
    }
}

void Planet::setTexture(const Texture *texture)
{
    getChild(getName())->setTexture(texture);
}

void Planet::onFrameChanged(double t)
{
    // time is in days
    Matrix m;

    /* FIXME complete the code */
    // 1) first calculate the position of the planet
    // orbit inclination (rotation)
    double I = m_orbitInclination*TO_RADIAN;
    m = Matrix::rotationX(I);
    Vector v = m*Vector::ORTHO_Z;

    // current angle around the sun (rotation)
    // use the current time and the rotation period
    double angle = (t/365.0)*PI_2/m_rotationPeriod;
    m = Matrix::rotation(angle, v);

    // distance from sun (translation)
    Vector orbit(m_distanceFromCenter, 0.0, 0.0);

    //Tourne vector orbit
    orbit = m*orbit;

    //Deplacer la planet et changer angle pour la planet face le centre le meme face
    m = Matrix::translation(orbit);

    /* STOP here */
    // store the resulting matrix. This will be used to paint the planet.
    setTransform(m);

    Matrix planetMatrix;
    /* FIXME complete the code */
    // 2) Then calculate the tilt and rotation of the sphere
    // Planet axial tilt (rotation)
    angle = m_axialTilt*TO_RADIAN;
    planetMatrix = Matrix::rotationZ(angle);
    //Rotation vector Z
    Vector v1 = planetMatrix*Vector::ORTHO_Z;

    //Initialiser angle pour tourner lui meme
    double angle1 = t*23.93/m_selfRotationPeriod*PI_2;

    //Rotation planet avec angle radian
    planetMatrix = Matrix::rotation(angle1, v1);

    /* STOP here */
    getChild(getName())->setTransform(planetMatrix);
}

